组件注册
- 关于组件名:
*组件名可以是kebab-case (短横线分隔命名)或者PascalCase (驼峰式命名)
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case
当使用 PascalCase (驼峰式命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。*
全局注册
//在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
Vue.component('my-component-name', {
// ... 选项 ...
})
局部注册
const component = {
template: `
<div>
<input type="text" v-model="text">
</div>
`,
data() {
return {
text: 123
}
},
methods:{
}
}
const app = new Vue({
//el:'#root',
components: {
comp: component
},
template: '<comp></comp>',
}).$mount("#root")
模板的要求
注意:组件的模板只能有一个根元素。下面的情况是不允许的。
template: `<div>这是一个局部的自定义组件,只能在当前Vue实例中使用</div>
<button>hello</button>`,
组件中的data必须是函数
当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:
data: {
count: 0
}
取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
data: function () {
return {
count: 0
}
}
如果 Vue 没有这条规则,所有模板都会共享一份数据。
解析 DOM 模板时的注意事项
有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。
这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
<table>
<blog-post-row></blog-post-row>
</table>
这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is 特性给了我们一个变通的办法:
<table>
<tr is="blog-post-row"></tr>
</table>
需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
字符串 (例如:template: '...')
单文件组件 (.vue)
<script type="text/x-template">
Prop
- Prop 类型
//普通类型
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
//指定类型
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object
}
单项数据流,传递过后不能修改
有两种常见的试图改变一个 prop 的情形:
//这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
//这个 prop 以一种原始的值传入且需要进行转换
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
Prop 验证
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 匹配任何类型)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组且一定会从一个工厂函数返回默认值
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
parent选项
类型:Vue instance
详细:
指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中。
//定义一个父组件
const parent = new Vue({
name: 'parent'
})
//定义一个组件
const componet2 = {
data () {
return {
text: 1
}
},
mounted () {
console.log(this.$parent.$options.name) //打印Root
this.$parent.text = 1234; //也可以修改,节制地使用 $parent 和 $children - 它们的主要目的是作为访问组件的应急方法。更推荐用 props 和 events 实现父子组件通信
}
}
//创建vue实例
new Vue({
parent: parent,
name: 'Root',
el: '#root',
mounted () {
console.log(this.$parent.$options.name) //此时打印出'parent'
},
components: {
Comp: componet2
},
data: {
text: 23333
},
template: `
<div>
<span>{{text}}</span>
<comp></comp>
</div>
`
})
组件继承
Vue.extend( options )
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
//包含组件选项的对象
const compoent = {
props: {
active: Boolean,
propOne: String
},
template: `
<div>
<input type="text" v-model="text">
<span @click="handleChange">{{propOne}}</span>
<span v-show="active">see me if active</span>
</div>
`,
data () {
return {
text: 0
}
},
mounted () {
console.log('comp mounted')
},
methods: {
handleChange () {
this.$emit('change')
}
}
}
// 创建构造器
const CompVue = Vue.extend(compoent)
// 创建 CompVue实例,并挂载到一个元素上。
new CompVue({
el: '#root',
propsData: { //传递props属性
propOne: 'xxx'
},
data: { //可以覆盖或者合并
text: '123'
},
mounted () { //不会覆盖 ,都会被调用
console.log('instance mounted')
}
})
自定义双向绑定
父子组件传递进行绑定
// 1定义一个组件对象
const component = {
props: ['value'],
template: `
<div>
<input type="text" @input="handleInput" :value="value"> //绑定传递过来的value
</div>
`,
methods: { //向父组件触发事件
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
//2定义一个实例
new Vue({
components: {
CompOne: component
},
el: '#root',
data () {
return {
value: '123'
}
},
//接受事件并改变value进行属性传递
template: `
<div>
<span>{{value}}</span>
<comp-one :value="value" @input="value = arguments[0]"></comp-one>
</div>
`
})
v-model绑定
const component = {
// model: {
// prop: 'value1',
// event: 'change'
// },
props: ['value'],
template: `
<div>
<input type="text" @input="handleInput" :value="value">
</div>
`,
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
new Vue({
components: {
CompOne: component
},
el: '#root',
data () {
return {
value: '123'
}
},
template: `
<div>
<span>{{value}}</span>
<comp-one v-model=value></comp-one> //直接使用v-model绑定
</div>
`
})
选项model
允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
import Vue from 'vue'
const component = {
model: {
prop: 'value1', //绑定value1
event: 'change'//绑定事件
},
props: ['value','value1'],
template: `
<div>
<input type="text" @change="handleInput" :value="value1">
</div>
`,
methods: {
handleInput (e) {
this.$emit('change', e.target.value)
}
}
}
new Vue({
components: {
CompOne: component
},
el: '#root',
data () {
return {
value: '123'
}
},
template: `
<div>
<span>{{value}}</span>
<comp-one v-model=value></comp-one>
</div>
`
})
非父子组件间的传值
兄弟间的组件传递:
1.使用vue官方提供的vuex(有一定难度,学习成本较大)
2.发布订阅模式(总线机制,Bus)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id = "app">
<child content="Dell"></child>
<child content="lee"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script>
//增加一个bus属性,指向vue的实例
Vue.prototype.bus = new Vue()
Vue.component('child',{
props:{
content:String
},
data:function(){
return {
seleContent:this.content
}
},
template:'<div @click="handleClick">{{seleContent}}</div>',
methods:{
handleClick(){
this.bus.$emit('change',this.seleContent)
}
},
mounted(){
let _this = this;
this.bus.$on('change',function(msg){
_this.seleContent = msg
})
}
})
new Vue({
el: "#app"
})
</script>
</body>
</html>
插槽
插槽内容
const component = {
template: `
<div :style="style">
<slot></slot> //vue的默认组件,放置插槽的地方
</div>
`,
data () {
return {
style: {
width: '200px',
height: '200px',
border: '1px solid #aaa'
},
value: 'component value'
}
}
}
new Vue({
components: {
CompOne: component
},
el: '#root',
data () {
return {
value: '123'
}
},
mounted () {
},
template: `
<div>
<comp-one>
<span>this is contne</span> //传递插槽内容
</comp-one>
</div>
`
})
具名插槽
有些时候我们需要多个插槽,可以进行命名;
import Vue from 'vue'
const component = {
template: `
<div :style="style">
<div class="header">
<slot name="header"></slot> //进行插槽命名
</div>
<div class="body">
<slot name="body"></slot>
</div>
</div>
`,
data () {
return {
style: {
width: '200px',
height: '200px',
border: '1px solid #aaa'
},
value: 'component value'
}
}
}
new Vue({
components: {
CompOne: component
},
el: '#root',
data () {
return {
value: '123'
}
},
mounted () {
},
template: `
<div>
<comp-one ref="comp">
<span slot="header">this is header</span> ///向指定的内容
<span slot="body">this is body</span>
</comp-one>
</div>
`
})
插槽的默认内容
可以在 <slot> 标签内部指定默认的内容来做到这一点。
<button type="submit">
<slot>Submit</slot>
</button>
如果父组件为这个插槽提供了内容,则默认的内容会被替换掉。
作用域插槽
import Vue from 'vue'
const component = {
template: `
<div :style="style">
<slot :value="value"></slot> //向外传递的数据
</div>
`,
data () {
return {
style: {
width: '200px',
height: '200px',
border: '1px solid #aaa'
},
value: 'component value'
}
}
}
new Vue({
components: {
CompOne: component
},
el: '#root',
data () {
return {
value: '123'
}
},
mounted () {
console.log(this.$refs.comp.value, this.$refs.span) //打印出组件实例和HTML标签
},
template: `
<div>
<comp-one ref="comp">
<span>{{value}}</span> //此时没有作用域,访问到的不是组件内的value,而是外部的value
//添加作用域,组件内部传递的数据都保存在props对象中
<span slot-scope="props" ref="span">{{props.value}}</span>
</comp-one>
</div>
`
})
动态组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id = "app">
<!-- <child-one v-if="type ==='child-one'"></child-one>
<child-two v-if="type ==='child-two'"></child-two> -->
<component :is="type"></component>
<button @click="handleBtn">change</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script>
Vue.component('child-one',{
template:'<div>child-one</div>'
})
Vue.component('child-two',{
template:'<div>child-two</div>'
})
new Vue({
el: "#app",
data:{
type:"child-one"
},
methods:{
handleBtn(){
this.type=this.type==="child-one"?"child-two":"child-one"
}
}
})
</script>
</body>
</html>
provide / inject 跨级组件通信
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
import Vue from 'vue'
const ChildComponent = {
template: '<div>child component:</div>',
inject: ['yeye','value'],
mounted () {
console.log(this.$parent.$options.name) //上一级
console.log(this.yeye,this.value) //上上一级,接受到的数据,打印出上上一级的vue实例和123
}
}
const component = {
name: 'comp',
components: {
ChildComponent
},
template: `
<div :style="style">
<slot :value="value"></slot>
<ChildComponent></ChildComponent>
</div>
`,
data () {
return {
style: {
width: '200px',
height: '200px',
border: '1px solid #aaa'
},
value: 'component value',
value2: 'component222 value'
}
}
}
new Vue({
components: {
CompOne: component
},
provide () { //和data一样,可以调用相应的值,是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。
return {
yeye: this,
value:this.value
}
},
el: '#root',
data () {
return {
value: '123'
}
},
mounted () {
console.log(this.$refs.comp.value2, this.$refs.span)
},
template: `
<div>
<comp-one ref="comp">
<span slot-scope="props" ref="span">{{props.value}}</span>
</comp-one>
</div>
`
})
provide 和 inject 绑定并不是可响应的。这是刻意为之的。provide 和 inject 绑定并不是可响应的。这是刻意为之的。
import Vue from 'vue'
const ChildComponent = {
template: '<div>child component:{{data.value}}</div>',
inject: ['yeye','data'],
mounted () {
console.log(this.$parent.$options.name) //上一级
console.log(this.yeye,this.value) //上一级
}
}
const component = {
name: 'comp',
components: {
ChildComponent
},
// template: `
// <div :style="style">
// <div class="header">
// <slot name="header"></slot>
// </div>
// <div class="body">
// <slot name="body"></slot>
// </div>
// </div>
// `,
template: `
<div :style="style">
<slot :value="value"></slot>
<ChildComponent></ChildComponent>
</div>
`,
data () {
return {
style: {
width: '200px',
height: '200px',
border: '1px solid #aaa'
},
value: 'component value',
value2: 'component222 value'
}
}
}
new Vue({
components: {
CompOne: component
},
provide () { //和data一样,可以调用相应的值
const data = {}
// es5双向绑定实现原理
Object.defineProperty(data, 'value', {
get: () => this.value, //获取最新value,每次调用value相当于get()方法
enumerable: true //可读取
})
return {
yeye: this,
// value:this.value
data
}
},
el: '#root',
data () {
return {
value: '123'
}
},
mounted () {
console.log(this.$refs.comp.value2, this.$refs.span)
},
template: `
<div>
<comp-one ref="comp">
<span slot-scope="props" ref="span">{{props.value}}</span>
</comp-one>
<input v-model="value" />
</div>
`
})
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
渲染函数 & JSX
参考:渲染函数&JSX
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。